package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

// 日志文件
var logFile *os.File

// 初始化日志
func init() {
	// 创建日志目录（如果不存在）
	logDir := "/data/servers/jnj/logs"
	if _, err := os.Stat(logDir); os.IsNotExist(err) {
		err := os.MkdirAll(logDir, 0755)
		if err != nil {
			fmt.Printf("Failed to create log directory: %v\n", err)
			return
		}
	}

	// 创建日志文件
	var err error
	logFile, err = os.OpenFile(filepath.Join(logDir, "app_manager.log"), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		fmt.Printf("Failed to open log file: %v\n", err)
		return
	}

	// 设置日志输出到文件和控制台
	log.SetOutput(logFile)
}

// StopApplication 停止应用服务
func StopApplication(app *Application) error {
	// 记录日志
	log.Printf("Checking stop script for application: %s\n", app.JarName)

	// 执行 Path/stop.sh
	stopScript := filepath.Join(app.Path, "stop.sh")
	log.Printf("Checking stop script: %s\n", stopScript)

	// 检查停止脚本是否存在
	if _, err := os.Stat(stopScript); os.IsNotExist(err) {
		log.Printf("Stop script does not exist, using fallback method for %s\n", app.JarName)
		// 如果stop.sh不存在，使用原来的kill命令
		cmd := exec.Command("sh", "-c",
			fmt.Sprintf("ps -ef|grep %s |grep -v grep |awk '{print $2}' |xargs kill", app.JarName))
		err := cmd.Run()
		if err != nil {
			// 如果没有找到进程，kill命令会返回错误，这里我们忽略这种错误
			log.Printf("Warning: error stopping application %s: %v\n", app.JarName, err)
		}
		return nil
	}

	// 执行停止脚本
	log.Printf("Executing stop script: %s\n", stopScript)
	cmd := exec.Command("sh", stopScript)
	output, err := cmd.CombinedOutput()
	log.Printf("Stop script output: %s\n", string(output))
	if err != nil {
		log.Printf("Warning: error executing stop script: %v\n", err)
		// 即使停止脚本出错，我们也继续执行更新流程
		return nil
	}

	log.Printf("Application stopped successfully: %s\n", app.JarName)
	return nil
}

// BackupApplication 备份旧的Jar文件
func BackupApplication(app *Application) error {
	// 记录日志
	log.Printf("Backing up application: %s\n", app.JarName)

	// 创建备份目录（如果不存在）
	backupDir := filepath.Join("/data/servers/backup")
	if _, err := os.Stat(backupDir); os.IsNotExist(err) {
		err := os.MkdirAll(backupDir, 0755)
		if err != nil {
			log.Printf("Failed to create backup directory: %v\n", err)
			return fmt.Errorf("failed to create backup directory: %v", err)
		}
	}

	// 源文件路径
	sourceFile := filepath.Join(app.Path, app.JarName)

	// 目标文件路径（带时间戳）
	timestamp := time.Now().Format("20060102_1504")
	backupFileName := fmt.Sprintf("%s_%s", timestamp, app.JarName)
	backupFile := filepath.Join(backupDir, backupFileName)

	// 执行备份: cp Path/JarName 用时间日期备份
	log.Printf("Copying %s to %s\n", sourceFile, backupFile)
	cmd := exec.Command("cp", sourceFile, backupFile)
	err := cmd.Run()
	if err != nil {
		log.Printf("Failed to backup application: %v\n", err)
		return fmt.Errorf("failed to backup application: %v", err)
	}

	log.Printf("Application backed up to: %s\n", backupFile)
	return nil
}

// StartApplication 启动应用
func StartApplication(app *Application) error {
	// 记录日志
	log.Printf("Starting application: %s\n", app.JarName)

	// 执行 Path/start.sh
	startScript := filepath.Join(app.Path, "start.sh")
	log.Printf("Checking start script: %s\n", startScript)

	// 检查启动脚本是否存在
	if _, err := os.Stat(startScript); os.IsNotExist(err) {
		errMsg := fmt.Sprintf("start script does not exist: %s", startScript)
		log.Printf(errMsg)
		return fmt.Errorf(errMsg)
	}

	// 执行启动脚本
	log.Printf("Executing start script: %s\n", startScript)
	cmd := exec.Command("sh", startScript)
	output, err := cmd.CombinedOutput()
	log.Printf("Start script output: %s\n", string(output))
	if err != nil {
		errMsg := fmt.Sprintf("failed to start application: %v, output: %s", err, string(output))
		log.Printf(errMsg)
		return fmt.Errorf(errMsg)
	}

	log.Printf("Application started successfully: %s\n", app.JarName)
	return nil
}

// RestartApplication 重启应用
func RestartApplication(app *Application) error {
	// 记录日志
	log.Printf("Restarting application on port %d...\n", app.Port)

	// 1. 停止服务
	log.Printf("Stopping application on port %d...\n", app.Port)
	err := StopApplication(app)
	if err != nil {
		errMsg := fmt.Sprintf("failed to stop application: %v", err)
		log.Printf(errMsg)
		return fmt.Errorf(errMsg)
	}

	// 等待一段时间确保应用完全停止
	log.Printf("Waiting for application to stop...\n")
	time.Sleep(3 * time.Second)

	// 2. 启动应用
	log.Printf("Starting application %s...\n", app.JarName)
	err = StartApplication(app)
	if err != nil {
		errMsg := fmt.Sprintf("failed to start application: %v", err)
		log.Printf(errMsg)
		return fmt.Errorf(errMsg)
	}

	log.Printf("Application restarted successfully: %s\n", app.JarName)
	return nil
}

// UpdateApplication 更新应用
func UpdateApplication(app *Application, newJarPath string) error {
	// 记录日志
	log.Printf("Starting update process for application %s on port %d\n", app.JarName, app.Port)

	// 1. 停止服务
	log.Printf("Stopping application on port %d...\n", app.Port)
	err := StopApplication(app)
	if err != nil {
		errMsg := fmt.Sprintf("failed to stop application: %v", err)
		log.Printf(errMsg)
		return fmt.Errorf(errMsg)
	}

	// 等待一段时间确保应用完全停止
	log.Printf("Waiting for application to stop...\n")
	time.Sleep(3 * time.Second)

	// 2. 备份旧的Jar文件
	log.Printf("Backing up application %s...\n", app.JarName)
	err = BackupApplication(app)
	if err != nil {
		errMsg := fmt.Sprintf("failed to backup application: %v", err)
		log.Printf(errMsg)
		return fmt.Errorf(errMsg)
	}

	// 3. 替换Jar文件
	sourceFile := filepath.Join(app.Path, app.JarName)
	log.Printf("Replacing jar file: %s\n", sourceFile)

	// 备份原始文件（以防更新失败）
	origBackup := sourceFile + ".orig"
	log.Printf("Creating backup of original file: %s\n", origBackup)
	cpCmd := exec.Command("cp", sourceFile, origBackup)
	cpCmd.Run() // 忽略错误，因为文件可能不存在

	// 复制新文件
	log.Printf("Copying new jar file from %s to %s\n", newJarPath, sourceFile)
	cpCmd = exec.Command("cp", newJarPath, sourceFile)
	err = cpCmd.Run()
	if err != nil {
		// 如果复制失败，尝试恢复原始文件
		log.Printf("Failed to copy new jar file, restoring original...\n")
		exec.Command("mv", origBackup, sourceFile).Run()
		errMsg := fmt.Sprintf("failed to copy new jar file: %v", err)
		log.Printf(errMsg)
		return fmt.Errorf(errMsg)
	}

	// 删除临时备份
	log.Printf("Removing temporary backup file\n")
	os.Remove(origBackup)

	// 4. 启动应用
	log.Printf("Starting application %s...\n", app.JarName)
	err = StartApplication(app)
	if err != nil {
		log.Printf("Failed to start application, attempting to restore original...\n")
		// 如果启动失败，尝试恢复原始文件并重启
		exec.Command("cp", origBackup, sourceFile).Run()
		StartApplication(app) // 尝试重新启动原始版本
		errMsg := fmt.Sprintf("failed to start application: %v", err)
		log.Printf(errMsg)
		return fmt.Errorf(errMsg)
	}

	log.Printf("Application updated successfully: %s\n", app.JarName)
	return nil
}

// UploadFrontend 上传前端代码
func UploadFrontend(front *Front, zipFilePath string) error {
	// 记录日志
	log.Printf("Starting frontend upload process for %s to %s\n", front.Name, front.Path)

	// 1. 创建备份目录（如果不存在）
	backupDir := filepath.Join("/data/servers/backup/frontend")
	if _, err := os.Stat(backupDir); os.IsNotExist(err) {
		err := os.MkdirAll(backupDir, 0755)
		if err != nil {
			errMsg := fmt.Sprintf("failed to create frontend backup directory: %v", err)
			log.Printf(errMsg)
			return fmt.Errorf(errMsg)
		}
	}

	// 2. 备份现有前端文件
	timestamp := time.Now().Format("20060102_1504")
	backupPath := filepath.Join(backupDir, fmt.Sprintf("%s_%s", timestamp, front.Name))

	// 检查前端目录是否存在
	if _, err := os.Stat(front.Path); err == nil {
		// 如果存在，备份现有文件
		log.Printf("Backing up existing frontend files from %s to %s\n", front.Path, backupPath)
		cpCmd := exec.Command("cp", "-r", front.Path, backupPath)
		err := cpCmd.Run()
		if err != nil {
			errMsg := fmt.Sprintf("failed to backup frontend files: %v", err)
			log.Printf(errMsg)
			return fmt.Errorf(errMsg)
		}
	} else {
		log.Printf("Frontend directory does not exist, creating new directory\n")
		// 创建前端目录
		err := os.MkdirAll(front.Path, 0755)
		if err != nil {
			errMsg := fmt.Sprintf("failed to create frontend directory: %v", err)
			log.Printf(errMsg)
			return fmt.Errorf(errMsg)
		}
	}

	// 3. 解压新的前端文件
	log.Printf("Extracting new frontend files from %s to %s\n", zipFilePath, front.Path)

	// 先清空目标目录中的内容（保留目录本身）
	rmCmd := exec.Command("rm", "-rf", filepath.Join(front.Path, "*"))
	rmCmd.Run() // 忽略错误

	// 解压zip文件
	unzipCmd := exec.Command("unzip", "-o", zipFilePath, "-d", front.Path)
	output, err := unzipCmd.CombinedOutput()
	log.Printf("Unzip output: %s\n", string(output))
	if err != nil {
		// 如果解压失败，尝试恢复备份
		log.Printf("Failed to extract frontend files, restoring backup...\n")
		if _, err := os.Stat(backupPath); err == nil {
			// 清空目标目录
			exec.Command("rm", "-rf", filepath.Join(front.Path, "*")).Run()
			// 恢复备份
			exec.Command("cp", "-r", filepath.Join(backupPath, filepath.Base(front.Path)), front.Path).Run()
		}
		errMsg := fmt.Sprintf("failed to extract frontend files: %v, output: %s", err, string(output))
		log.Printf(errMsg)
		return fmt.Errorf(errMsg)
	}

	log.Printf("Frontend files uploaded and extracted successfully to %s\n", front.Path)
	return nil
}

// RestartNginx 重启Nginx服务
func RestartNginx() error {
	// 记录日志
	log.Println("Restarting Nginx service...")

	// 1. 测试Nginx配置
	log.Println("Testing Nginx configuration...")
	testCmd := exec.Command("nginx", "-t")
	testOutput, err := testCmd.CombinedOutput()
	log.Printf("Nginx test output: %s\n", string(testOutput))
	if err != nil {
		errMsg := fmt.Sprintf("nginx configuration test failed: %v, output: %s", err, string(testOutput))
		log.Printf(errMsg)
		return fmt.Errorf(errMsg)
	}

	// 2. 重新加载Nginx配置
	log.Println("Reloading Nginx...")
	reloadCmd := exec.Command("nginx", "-s", "reload")
	reloadOutput, err := reloadCmd.CombinedOutput()
	log.Printf("Nginx reload output: %s\n", string(reloadOutput))
	if err != nil {
		errMsg := fmt.Sprintf("failed to reload nginx: %v, output: %s", err, string(reloadOutput))
		log.Printf(errMsg)
		return fmt.Errorf(errMsg)
	}

	log.Println("Nginx restarted successfully")
	return nil
}

// GetApplicationLogPath 获取应用日志文件路径
func GetApplicationLogPath(app *Application) string {
	// 如果配置了日志路径，则直接返回配置的日志路径
	if app.LogPath != "" {
		return app.LogPath
	}
	// 否则使用默认路径
	return filepath.Join(app.Path, "nohup.out")
}

// TailApplicationLog 实时查看应用日志 (类似 tail -f)
func TailApplicationLog(app *Application, lines int) ([]string, error) {
	logPath := GetApplicationLogPath(app)

	// 检查日志文件是否存在
	if _, err := os.Stat(logPath); os.IsNotExist(err) {
		return nil, fmt.Errorf("log file does not exist: %s", logPath)
	}

	// 使用tail命令获取最后几行日志
	cmd := exec.Command("tail", "-n", fmt.Sprintf("%d", lines), logPath)
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("failed to read log file: %v", err)
	}

	// 按行分割输出
	logLines := []string{}
	scanner := bufio.NewScanner(strings.NewReader(string(output)))
	for scanner.Scan() {
		logLines = append(logLines, scanner.Text())
	}

	return logLines, nil
}

// ReadApplicationLog 读取应用日志文件
func ReadApplicationLog(app *Application, offset int64, length int) (string, error) {
	logPath := GetApplicationLogPath(app)

	// 检查日志文件是否存在
	if _, err := os.Stat(logPath); os.IsNotExist(err) {
		return "", fmt.Errorf("log file does not exist: %s", logPath)
	}

	// 打开日志文件
	file, err := os.Open(logPath)
	if err != nil {
		return "", fmt.Errorf("failed to open log file: %v", err)
	}
	defer file.Close()

	// 移动到指定偏移位置
	_, err = file.Seek(offset, 0)
	if err != nil {
		return "", fmt.Errorf("failed to seek log file: %v", err)
	}

	// 读取指定长度的内容
	buffer := make([]byte, length)
	n, err := file.Read(buffer)
	if err != nil && err != io.EOF {
		return "", fmt.Errorf("failed to read log file: %v", err)
	}

	return string(buffer[:n]), nil
}

// DownloadApplicationLog 下载应用日志文件
func DownloadApplicationLog(app *Application) (string, error) {
	logPath := GetApplicationLogPath(app)

	// 检查日志文件是否存在
	if _, err := os.Stat(logPath); os.IsNotExist(err) {
		return "", fmt.Errorf("log file does not exist: %s", logPath)
	}

	return logPath, nil
}
